import "reflect-metadata"
import { getBeanId, getClass, getClassName } from "../common"
import { BeanType } from "./BeanType"

const IOC_BEAN_KEY = 'ioc:bean-key'
const IOC_COMPONENT_KEY = 'ioc:component-key'
const IOC_CONFIGURATION_KEY = 'ioc:configuration-key'
const IOC_INJECT_KEY = 'ioc:prop-key'
const IOC_VALUE_KEY = 'ioc:value-key'
const IOC_ROUTER_KEY = 'ioc:router-key'
const IOC_PARAM_KEY = 'ioc:router-param-key'

export abstract class ReflectUtil {
  static get(key: string, target: any, propertyKey: string | symbol): any {
    return Reflect.getMetadata(key, getClass(target), propertyKey)
  }

  static set(
    key: string,
    value: any,
    target: any,
    propertyKey?: string | symbol
  ): any {
    return propertyKey
      ? Reflect.defineMetadata(key, value, getClass(target), propertyKey)
      : Reflect.defineMetadata(key, value, getClass(target))
  }

  static getOwn(key: string, target: any, propertyKey?: string | symbol): any {
    return propertyKey
      ? Reflect.getOwnMetadata(key, getClass(target), propertyKey)
      : Reflect.getOwnMetadata(key, getClass(target))
  }

  // 获取成员类型
  static getType(target: any, propertyKey?: string | symbol) {
    return Reflect.getMetadata("design:type", target, propertyKey)
  }

  // 获取成员参数类型
  static getParamTypes(target: any, propertyKey?: string | symbol) {
    return Reflect.getMetadata("design:paramtypes", target, propertyKey)
  }

  // 获取成员返回类型
  static getReturnType(target: any, propertyKey?: string | symbol) {
    return Reflect.getMetadata("design:returntype", target, propertyKey)
  }

  // private static methodParamsMap = new Map<String, Array<any>>()
  //
  // static defineParam(methodKey: string, params: Array<any>) {
  //   console.log(`Reflect defineParam ==> methodKey = ${methodKey}, params = ${params}`)
  //   ReflectUtil.methodParamsMap.set(methodKey, params)
  // }
  //
  // static getParams(methodKey: string) {
  //   return ReflectUtil.methodParamsMap.get(methodKey)
  // }

  // @Bean (根据依赖关系构建Bean)
  private static beanMap = new Map<String, any>()

  static defineBean(id: string, returnType, paramTypes, constructFn) {
    if (ReflectUtil.beanMap.get(id)) {
      throw new Error(`Reflect defineBean => id = ${id} 冲突.`)
    }
    console.log(`Reflect defineBean id = ${id}, returnType = ${returnType.name}, paramTypes = `, paramTypes)
    ReflectUtil.beanMap.set(id, { id, returnType, paramTypes, constructFn })
  }

  static getBeans() {
    return [...ReflectUtil.beanMap.values()]
  }

  // @Component, @Controller, @Service, @Repository, @Bean
  static defineComponent(target: Object, beanType: BeanType, id: string = null, args: any = null) {
    if (id == null) {
      id = getBeanId(target)
    }
    console.log(`Reflect defineComponent id = ${id}, target = ${getClassName(target)}, args = `, args)
    Reflect.defineMetadata(IOC_COMPONENT_KEY, {
      beanType,
      clazz: target,
      id,
      args
    }, target)
  }

  static getComponent(target: Object) {
    return Reflect.getMetadata(IOC_COMPONENT_KEY, target)
  }

  // @Inject
  static defineInject(target: Object, props: object) {
    console.log(`Reflect defineInject ==> class = ${getClassName(target)}, props = `, Object.keys(props),
      ' clazz = ', Object.values(props).map(it => it.clazz))
    Reflect.defineMetadata(IOC_INJECT_KEY, props, target)
  }

  static getInjects(target: Object) {
    return Reflect.getMetadata(IOC_INJECT_KEY, target)
  }

  static hasInject(target: Object) {
    return Reflect.hasOwnMetadata(IOC_INJECT_KEY, target)
  }

  // @Configuration
  static defineConfiguration(target: Object, props: Object) {
    console.log(`Reflect defineConfiguration ==> class = ${getClassName(target)}, props = `, props)
    Reflect.defineMetadata(IOC_CONFIGURATION_KEY, props, target)
  }

  static getConfiguration(target: Object) {
    return Reflect.getMetadata(IOC_CONFIGURATION_KEY, target)
  }

  // @Value
  static defineValue(target: Object, props: any) {
    console.log(`Reflect defineValue ==> class = ${getClassName(target)}, [${props.map(it => `${it.prop} = ${it.path}`)}]`)
    Reflect.defineMetadata(IOC_VALUE_KEY, props, target)
  }

  static getValues(target: Object) {
    return Reflect.getMetadata(IOC_VALUE_KEY, target)
  }

  // @Router
  static defineRouter(target: Object, props: Object) {
    console.log(`Reflect defineRouter ==> class = ${getClassName(target)}`, Object.keys(props))
    Reflect.defineMetadata(IOC_ROUTER_KEY, props, target)
  }

  static getRouters(target: Object) {
    return Reflect.getMetadata(IOC_ROUTER_KEY, target)
  }

  // @Router Param
  // static defineParam(target: Object, propertyKey: string, props: Object) {
  //   console.log(`Reflect defineParam ==> class = ${getClassName(target)}, method = ${propertyKey}, props = `, Object.keys(props))
  //   Reflect.defineMetadata(IOC_PARAM_KEY, props, target, propertyKey)
  // }
  //
  // static getParams(target: Object, propertyKey: string) {
  //   return Reflect.getMetadata(IOC_PARAM_KEY, target, propertyKey)
  // }

  // method 信息
  private static methodParamsMap = new Map<String, Array<any>>()

  static defineParam(methodKey: string, params: Array<any>) {
    if (ReflectUtil.methodParamsMap.get(methodKey)) {
      throw new Error("Reflect defineParam => methodKey = ${methodKey} 冲突.")
    }
    console.log(`Reflect defineParam ==> methodKey = ${methodKey}, params = ${params}`)
    ReflectUtil.methodParamsMap.set(methodKey, params)
  }

  static getParams(methodKey: string) {
    return ReflectUtil.methodParamsMap.get(methodKey)
  }

  //
  static getPropertyDescriptor(
    target: any,
    propertyKey: string
  ): PropertyDescriptor | undefined {
    const classOrFn = (target && target.prototype) || target
    return Reflect.getOwnPropertyDescriptor(classOrFn, propertyKey)
  }
}
